/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.rowan.adi.components.comp2d;

import java.util.LinkedList;
import org.rowan.adi.Vector;
import org.rowan.adi.components.comp2d.Point2D;

/*
 * The Bezier Curve class represents a curved line in two dimensional space.
 * A Bezier Curve is comprised of two endpoints as well as two control points.
 * The two control points determine the curvature of the bezier curve.
 * This structure handles  high-order curves which do not include Fourth-Order Curves.
 * Bezier Curve Expressions
 * Linear Bezier Curves
 * B(t) = P0 + t * (P1 - P0) = (1-t)P0 + tP1 , t ->[0,1]
 * Quadratic  Bezier Curves
 * B(t) = (1-t)^2 * P0  + 2(1-t)tP1 + t^2 * P2 , t ->[0,1]
 *
 * @author Mike
 */
public class BezierCurve2D  extends Component2D {

    private double length;
    static double accuaryOfPoints = .01; //smaller is more accuarate (must be %1==0)
    LinkedList<Point2D> points = new LinkedList<Point2D>();


    /**
     * Constructor for a Cubic Bezier Curve
     * @param endpoint1 A 2D point representing an end point of the Bezier Curve.
     * @param endpoint2 A 2D point representing an end point of the Bezier Curve.
     * @param controlPoint1 A 2D point which acts as a control point for  the Bezier Curve.
     * @param controlPoint2 A 2D point which acts as a control point for  the Bezier Curve.
     */
    public BezierCurve2D(Point2D endpoint1,Point2D controlpoint1,Point2D controlpoint2, Point2D endpoint2) {
          LinkedList<Point2D> listOfPoints = new LinkedList<Point2D>();
          listOfPoints.add(endpoint1);
          listOfPoints.add(controlpoint1);
          listOfPoints.add(controlpoint2);
          listOfPoints.add(endpoint2);

          this.points = listOfPoints;
          super.setListofPoints(points);
          //super.setLocation(new Vector((point2.getX() + point1.getX()) / 2, (point2.getY() + point1.getY()) / 2));
          super.setTrajectory(Vector.ZERO_VECTOR);
          updateLength();
     }
     /**
     * Constructor for a Quadratic Bezier Curve
     * @param endpoint1 A 2D point representing an end point of the Bezier Curve.
     * @param endpoint2 A 2D point representing an end point of the Bezier Curve.
     * @param controlPoint1 A 2D point which acts as a control point for  the Bezier Curve.
     */
    public BezierCurve2D(Point2D endpoint1,Point2D controlpoint1, Point2D endpoint2) {
          LinkedList<Point2D> listOfPoints = new LinkedList<Point2D>();
          listOfPoints.add(endpoint1);
          listOfPoints.add(controlpoint1);
          listOfPoints.add(endpoint2);

          this.points = listOfPoints;
         super.setListofPoints(points);
         //super.setLocation(new Vector((point2.getX() + point1.getX()) / 2, (point2.getY() + point1.getY()) / 2));
         super.setTrajectory(Vector.ZERO_VECTOR);
         updateLength();
     }
    /**
     * Constructor for a Higher-Order Bezier Curve
     * @param points List of Points in sequence of EndPoint1 ControlPoints..(1...n) EndPoint2
     */
     public BezierCurve2D(LinkedList<Point2D> points) throws Exception {
         if(points.size() < 3){
             throw new Exception("Not enough Points for a Bezier Curve");
         }
         this.points = points;

         super.setListofPoints(points);
         //super.setLocation(new Vector((point2.getX() + point1.getX()) / 2, (point2.getY() + point1.getY()) / 2));
         super.setTrajectory(Vector.ZERO_VECTOR);
         updateLength();
     }



    //=============Methods============


    public double areaUnderCurve(){
        //Parametric Equations




        return 0.0;
    }
    public double getLength(){
       return length;
    }

    //=============Private=============
    public boolean isLegal(){
        return true;
    }

    private void updateLength(){
      double updateLength = 0.0;
        for(double t = 0.0; t < 1;t+=accuaryOfPoints){
           LineSegment2D line = new LineSegment2D(pointAtT(t,0,points.size()-1,points),pointAtT(t+accuaryOfPoints,0,points.size()-1,points));
           updateLength += line.getLength();
        }
      this.length = updateLength;
    }
    private Point2D pointAtT(double t,int start,int finish,LinkedList<Point2D> points){
           //base case
           if(Math.abs(start - finish) == 2){
               double current_bx;
               double current_by;

               current_bx = (1-t)*(1-t)*points.get(start).getX();
               current_by = (1-t)*(1-t)*points.get(start).getY();

               current_bx+= 2*(1-t)*t*points.get(start+1).getX();
               current_by+= 2*(1-t)*t*points.get(start+1).getY();

               current_bx+= t*t*points.get(finish).getX();
               current_by+= t*t*points.get(finish).getY();

               return new Point2D(new Vector(current_bx,current_by));

           } else {
               //Recusivley calculate the equation for the bezier curve. Could be quadratic, cubic or higher
               double calculated_X = (1-t) * pointAtT(t,0,finish-1,points).getX() + t *  pointAtT(t,start+1,finish,points).getX();
               double calculated_Y = (1-t) * pointAtT(t,0,finish-1,points).getY() + t *  pointAtT(t,start+1,finish,points).getY();
               return new Point2D(new Vector(calculated_X,calculated_Y));
           }
       }
    
    public static void main(String []args){
        Point2D e1 = new Point2D(new Vector(0,1));
        Point2D c1 = new Point2D(new Vector(1,1));
        Point2D c2 = new Point2D(new Vector(1,1));
        Point2D e2 = new Point2D(new Vector(1,0));

        BezierCurve2D b = new BezierCurve2D(e1,c1,e2);

        System.out.println("Length is " + b.getLength());

    }
}
